/**
 *  Copyright (C) 2006 zhangbo (freeeob@gmail.com)
 *
 *  This product is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU Lesser General Public License as published by
 *  the Free Software Foundation; either version 2.1 of the License, or
 *  (at your option) any later version.
 * 
 *  This product is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU Lesser General Public License for more details.
 * 
 *  You should have received a copy of the GNU Lesser General Public License
 *  along with this library; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA.
 *
 *  author:zhangbo
 *  Email:jsjava@gmail.com
 */

/**  The Fraction class references to org.apache.commons.math.fraction.Fraction */

/**
 * Create a fraction given the numerator and denominator.
 * @param num
 * @param den
 */
function Fraction(num,den){
	this.jsjava_class="org.apache.commons.math.fraction.Fraction";
	if (den == 0) {
        throw new ArithmeticException(ArithmeticException.ERROR,"The denominator must not be zero");
    }
    if (den < 0) {
        if (num == Integer.MIN_VALUE ||
                den == Integer.MIN_VALUE) {
            throw new ArithmeticException(ArithmeticException.ERROR,"overflow: can't negate");
        }
        num = -num;
        den = -den;
    }
    this.numerator = num;
    this.denominator = den;
    var d = MathUtils.gcd(this.numerator, this.denominator);
    if (d > 1) {
        this.numerator /= d;
        this.denominator /= d;
    }
    if (this.denominator < 0) {
        this.numerator *= -1;
        this.denominator *= -1;
    }
}

Fraction.ONE = new Fraction(1, 1);
Fraction.ZERO = new Fraction(0, 1);

/**
 * Returns the absolute value of this fraction.
 */
Fraction.prototype.abs=function(){
	var ret;
    if (this.numerator >= 0) {
        ret = this;
    } else {
        ret = this.negate();
    }
    return ret;
};

/**
 * Compares this object to another based on size.
 * @param o
 */
Fraction.prototype.compareTo=function(o){
	if(!o||!o.jsjava_class||o.jsjava_class!="org.apache.commons.math.fraction.Fraction"){
		return -1;
	}
	var ret = 0;   
    var first = this.doubleValue();
    var second = o.doubleValue();    
    if (first < second) {
        ret = -1;
    } else if (first > second) {
        ret = 1;
    }
    return ret;
};

/**
 * Gets the fraction as a double.
 */
Fraction.prototype.doubleValue=function(){
	return this.numerator / this.denominator;
};

/**
 * Test for the equality of two fractions.
 * @param o
 */
Fraction.prototype.equals=function(o){
	if(!o||!o.jsjava_class||o.jsjava_class!="org.apache.commons.math.fraction.Fraction"){
		return false;
	}
	if(this.numerator==o.numerator&&this.denominator==o.denominator){
		return true;
	}
	return false;
};

/** 
 * Gets the fraction as a float.
 */
Fraction.prototype.floatValue=function(){
	return this.doubleValue();
};

/**
 * Access the denominator.
 */
Fraction.prototype.getDenominator=function(){
	return this.denominator;
};

/**
 * Access the numerator.
 */
Fraction.prototype.getNumerator=function(){
	return this.numerator;
};

/**
 * Gets the fraction as an int.
 */
Fraction.prototype.intValue=function(){
	return Math.round(this.doubleValue());
};

/**
 * Gets the fraction as a long.
 */
Fraction.prototype.longValue=function(){
	return Math.round(this.doubleValue());
};

/**
 * Return the additive inverse of this fraction.
 */
Fraction.prototype.negate=function(){
	if (this.numerator==Integer.MIN_VALUE) {
        throw new ArithmeticException(ArithmeticException.ERROR,"overflow: too large to negate");
    }
    return new Fraction(-this.numerator, this.denominator);
};

/**
 * Return the multiplicative inverse of this fraction.
 */
Fraction.prototype.reciprocal=function(){
	return new Fraction(this.denominator, this.numerator);
};

/**
 * Adds the value of this fraction to another, returning the result in reduced form.
 * @param fraction
 */
Fraction.prototype.add=function(fraction){
	return this.addSub(fraction, true);
};

/**
 * Subtracts the value of another fraction from the value of this one, returning the result in reduced form.
 * @param fraction
 */
Fraction.prototype.subtract=function(fraction){
	return this.addSub(fraction, false);
};

/**
 * Implement add and subtract using algorithm described in Knuth 4.5.1.
 * @param fraction
 * @param isAdd
 */
Fraction.prototype.addSub=function(fraction,isAdd){
	if (fraction == null) {
        throw new IllegalArgumentException(IllegalArgumentException.ERROR,"The fraction must not be null");
    }
    if (this.numerator == 0) {
        return isAdd ? fraction : fraction.negate();
    }
    if (fraction.numerator == 0) {
        return this;
    }     
    var d1 = MathUtils.gcd(this.denominator, fraction.denominator);
    if (d1==1) {
        var uvp = this.numerator*fraction.denominator;
        var upv = fraction.numerator*this.denominator;
        return new Fraction
            (isAdd ? (uvp+upv) : (uvp-upv),this.denominator*fraction.denominator);
    }
    var uvp = this.numerator*fraction.denominator/d1;
    var upv = fraction.numerator*this.denominator/d1;
    var t = isAdd ? (uvp+upv) : (uvp-upv);
    var tmodd1 = t%d1;
    var d2 = (tmodd1==0)?d1:MathUtils.gcd(tmodd1, d1);

    var w = t/d2;
    return new Fraction (w, this.denominator/d1*fraction.denominator/d2);
};

/**
 * Multiplies the value of this fraction by another, returning the result in reduced form.
 * @param fraction
 */
Fraction.prototype.multiply=function(fraction){
	if (fraction == null) {
        throw new IllegalArgumentException(IllegalArgumentException.ERROR,"The fraction must not be null");
    }
    if (this.numerator == 0 || fraction.numerator == 0) {
        return Fraction.ZERO;
    }
    var d1 = MathUtils.gcd(this.numerator, fraction.denominator);
    var d2 = MathUtils.gcd(fraction.numerator, this.denominator);
    return Fraction.getReducedFraction
    (this.numerator/d1*fraction.numerator/d2,
            this.denominator/d2*fraction.denominator/d1);
};

/**
 * Divide the value of this fraction by another.
 * @param divide
 */
Fraction.prototype.divide=function(fraction){
	if (fraction==undefined||fraction == null) {
        throw new IllegalArgumentException(IllegalArgumentException.ERROR,"The fraction must not be null");
    }
    if (fraction.numerator == 0) {
        throw new ArithmeticException(IllegalArgumentException.ERROR,"The fraction to divide by must not be zero");
    }
    return this.multiply(fraction.reciprocal());
};

/**
 * Creates a Fraction instance with the 2 parts of a fraction Y/Z.
 * @param numerator
 * @param denominator
 */
Fraction.getReducedFraction=function(numerator,denominator){
	if (denominator == 0) {
        throw new ArithmeticException(ArithmeticException.ERROR,"The denominator must not be zero");
    }
    if (numerator==0) {
        return Fraction.ZERO; 
    }
    if (denominator==Integer.MIN_VALUE && (numerator&1)==0) {
        numerator/=2; denominator/=2;
    }
    if (denominator < 0) {
        if (numerator==Integer.MIN_VALUE ||
                denominator==Integer.MIN_VALUE) {
            throw new ArithmeticException(ArithmeticException.ERROR,"overflow: can't negate");
        }
        numerator = -numerator;
        denominator = -denominator;
    }
    var gcd = MathUtils.gcd(numerator, denominator);
    numerator /= gcd;
    denominator /= gcd;
    return new Fraction(numerator, denominator);
};

/**
 * return a string description
 */
Fraction.prototype.toString=function(){
	return this.numerator+"/"+this.denominator;
};